package org.orbis.memoryblock.skippointer;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.orbis.constants.Constants;
import org.orbis.memoryblock.MemoryBlock;
import org.orbis.structure.LookupPointer;
import org.orbis.toolbox.ByteConverter;

/** Implements skip pointer for content.<br>
 * Data structure as follows:
 * <dl>
 * <dt>[Block]</dt>
 * <dd> --&gt; &lt;[ID][Point]&gt;*</dd>
 * <dt>[ID]</dt>
 * <dd> --&gt; (8-byte digits)</dd>
 * <dt>[Point]</dt>
 * <dd> --&gt; (Pair of 3-byte digits, represents pointer to content field data)</dd>
 * </dl>
 * 
 * @author iamyusk */
public class ContentSkipPointerBlock extends MemoryBlock{
	public static final int OPT_NBLOCK = 0;
	public static final int OPT_COUNT = 1;
	public static final int OPT_SUBTYPE = 2;
	public static final int OPT_PBLOCK = 3;
	public static final int OPTSIZE = 4;

	public ContentSkipPointerBlock(ByteBuffer body, int bodySize, int ID, int cluster){
		super(body, bodySize, ID, Constants.MEMORY_TYPE_SKIPPTRCONT, cluster);
	}

	public ContentSkipPointerBlock(ByteBuffer existingBody){
		super(existingBody);
	}

	public synchronized int allocate(){
		if(this.bodyVacancy < PTR + LONG) return -1;

		this.bodyVacancy -= PTR + LONG;
		this.option[OPT_COUNT]++;

		return this.bodySize - this.bodyVacancy - PTR - LONG;
	}

	public long getID(int point){
		if(point + LONG + PTR > this.bodySize - this.bodyVacancy) return -1;

		long value = getLong(point, LONG);

		return value;
	}

	public LookupPointer getPoint(int point){
		if(point + PTR > this.bodySize - this.bodyVacancy) return null;

		LookupPointer value = getPointer(point, PTR);

		return value;
	}

	public int modifyPointer(long fromID, LookupPointer pointerToContent, int index){
		int point = index * (LONG + PTR);
		// Check the block is empty
		if(this.bodyVacancy == this.bodySize){
			this.bodyVacancy -= LONG + PTR;
			this.option[OPT_COUNT]++;
		}
		// Check the point indicates existing data
		if(point + LONG + PTR > this.bodySize - this.bodyVacancy){
			int left = point - (this.bodySize - this.bodyVacancy);
			return left / (LONG + PTR);
		}

		setLong(point, LONG, fromID);
		point += LONG;
		setPointer(point, PTR, pointerToContent);
		return 0;
	}

	public void setPointer(long fromID, LookupPointer pointerToContent, int point){
		setLong(point, LONG, fromID);

		point += LONG;

		setPointer(point, PTR, pointerToContent);
		option[OPT_COUNT]++;
	}
	
	public int getFinalPoint() {
		if(this.bodySize == this.bodyVacancy) return 0;
		return this.bodySize - this.bodyVacancy - LONG - PTR;
	}

	@Override
	public int getOption(int num){
		if(num >= OPTSIZE) return 0;

		return option[num];
	}

	@Override
	public int getOptionSize(){
		return OPTSIZE;
	}

	@Override
	public byte[] getOptionByteStream(){
		ByteArrayOutputStream stream = new ByteArrayOutputStream(OPTSIZE * INT);

		for(int i = 0; i < OPTSIZE; i++){
			try{
				stream.write(ByteConverter.intToByte(option[i], INT));
			}catch(IOException e){}
		}

		return stream.toByteArray();
	}

	@Override
	public void setOption(int index, int value){
		if(index >= OPTSIZE) return;

		option[index] = value;
	}

	@Override
	public String getClassName(){
		return "SkipPointer for content";
	}

}
